﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Threading;
using Sinacor.Infra.UI.Controls.SmartClient;
using Sinacor.Infra.UI.Messaging.SmartClient;
using Sinacor.Infra.UI.Messaging.Internal;
using System.Threading;
using Sinacor.Infra.UI.Messaging;
 
namespace Sinacor.Infra.UI.Desktop.SmartClient
{
    public class WindowManager : DispatcherObject, IDisposable
    {
        private static Mutex mtx = new Mutex();

        private static WindowManager _windowManager = null;

        private Dictionary<int, SinacorWindow> _dctWindowsOpened = new Dictionary<int, SinacorWindow>();
        private WPFMessageManager _wpfMessageManager = new WPFMessageManager();

        public delegate void OnLoadWindowDelegate(SinacorWindow sinacorwindow);
        public event OnLoadWindowDelegate OnLoadWindow;

        public delegate void OnUnloadWindowDelegate(SinacorWindow sinacorwindow);
        public event OnUnloadWindowDelegate OnUnloadWindow;

        public delegate void OnWindowShortCutRequestedDelegate(SinacorWindow sinacorwindow);
        public event OnWindowShortCutRequestedDelegate OnWindowShortCutRequested;

        public delegate void OnWindowActivityDelegate();
        public event OnWindowActivityDelegate OnWindowActivity;

        public delegate void OnWindowManagerErrorDelegate(Exception e);
        public event OnWindowManagerErrorDelegate OnWindowManagerError;

        //TODO: Controlar dicionários por empresa.
        protected WindowManager()
        {
            _wpfMessageManager.OnInternalMessageArrival += new WPFMessageManager.WPFInternalMessageDelegate(_wpfMessageManager_OnInternalMessageArrival);
        }

        public static WindowManager GetInstance()
        {
            mtx.WaitOne();
            if (_windowManager == null)
                _windowManager = new WindowManager();
            mtx.ReleaseMutex();

            return _windowManager;
        }

        #region Recebe e envia mensagens
        private void _wpfMessageManager_OnInternalMessageArrival(InternalMessage internalMessage)
        {
            //Parte da resolução de Leek de Memória da aplicação
            try
            {
                switch (internalMessage.MessageId)
                {
                    case MessageIds.WindowManager.SNC_WINDOW_OPEN: 
                        ProcessaAberturaJanela((SinacorWindow)internalMessage.Message);
                        internalMessage.Message = null;
                        break;
                    case MessageIds.WindowManager.SNC_WINDOW_CLOSE: 
                        ProcessaFechamentoJanela((SinacorWindow)internalMessage.Message);
                        internalMessage.Message = null;
                        break;
                    case MessageIds.WindowManager.SNC_WINDOW_AS_SHORTCUT: 
                        ProcessaJanelaComoShortCut((SinacorWindow)internalMessage.Message);
                        internalMessage.Message = null;
                        break;
                    case MessageIds.WindowManager.SNC_WINDOW_ACTIVITY:
                        ProcessaAtividadeJanela();
                        break;
                }
            }
            catch (Exception e)
            {
                OnWindowManagerError(e);
            }
        }

        //public void NotifyApplicationClose()
        //{
        //    InternalMessage internalMessage = new InternalMessage();
        //    internalMessage.MessageId = MessageIds.Application.SNC_APPLICATION_CLOSE;
        //    internalMessage.Message = null;
        //    _wpfMessageManager.SendMessage(internalMessage);
        //}

        public void NotifyApplicationUnblocked()
        {
            InternalMessage internalMessage = new InternalMessage();
            internalMessage.MessageId = MessageIds.Application.SNC_APPLICATION_UNBLOCKED;
            internalMessage.Message = null;
            _wpfMessageManager.SendMessage(internalMessage);
        }

        public void NotifyApplicationInactivity()
        {
            InternalMessage internalMessage = new InternalMessage();
            internalMessage.MessageId = MessageIds.Application.SNC_APPLICATION_INACTIVITY;
            internalMessage.Message = null;
            _wpfMessageManager.SendMessage(internalMessage);
        }

        public void NotifyDesktopVirtualChanged(string tokenID)
        {
            InternalMessage internalMessage = new InternalMessage();
            internalMessage.MessageId = MessageIds.DesktopVirtual.SNC_WINDOW_DSKTP_VRTL_CHANGED;
            internalMessage.Message = (object)tokenID;
            _wpfMessageManager.SendMessage(internalMessage);
        }

        #endregion

        # region Processa Mensagem e Dispara Evento        
        /// <summary>
        /// Método que dispara o evento de WindowActivity quando alguma atividade é detectada
        /// sobre a window
        /// </summary>
        //private void ProcessaAtividadeJanela(SinacorWindow sinacorWindow)
        private void ProcessaAtividadeJanela()
        {
            try
            {
                if (OnWindowActivity != null)
                    this.Dispatcher.BeginInvoke(OnWindowActivity, DispatcherPriority.Normal);
            }
            catch (Exception e)
            {
                OnWindowManagerError(e);
            }
        }

        private void ProcessaJanelaComoShortCut(SinacorWindow sinacorWindow)
        {
            try
            {
                if (OnWindowShortCutRequested != null)
                    this.Dispatcher.BeginInvoke(DispatcherPriority.Normal, OnWindowShortCutRequested, sinacorWindow);
            }
            catch (Exception e)
            {
                OnWindowManagerError(e);
            }
        }

        private void ProcessaAberturaJanela(SinacorWindow sinacorWindow)
        {
            // Gera a chave de indentificação única da janela.
            int intHashCodeAsKey = sinacorWindow.GetHashCode();

            // Se ainda não existir no dicionário, adiciona
            if (_dctWindowsOpened != null)
            {
                lock (_dctWindowsOpened)
                {
                    if (!_dctWindowsOpened.ContainsKey(intHashCodeAsKey))
                        _dctWindowsOpened.Add(intHashCodeAsKey, sinacorWindow);
                    else // Se já existir, faz update
                        _dctWindowsOpened[intHashCodeAsKey] = sinacorWindow;
                }
            }

            try
            {
                // Dispara um evento avisando que uma janela foi aberta.
                if (OnLoadWindow != null)
                    this.Dispatcher.Invoke(DispatcherPriority.Send, OnLoadWindow, sinacorWindow);
            }
            catch (Exception e)
            {
                OnWindowManagerError(e);
            }

        }

        private void ProcessaFechamentoJanela(SinacorWindow sinacorWindow)
        {
            // Gera a chave de indentificação única da janela.
            int intHashCodeAsKey = sinacorWindow.GetHashCode();

            // Se existir no dicionário, remove
            if (_dctWindowsOpened != null)
            {
                lock (_dctWindowsOpened)
                {
                    if (_dctWindowsOpened.ContainsKey(intHashCodeAsKey))
                        _dctWindowsOpened.Remove(intHashCodeAsKey);
                }
            }

            try
            {
                // Dispara um evento avisando que uma janela foi fechada.
                if (OnUnloadWindow != null)
                    this.Dispatcher.Invoke(DispatcherPriority.Send, OnUnloadWindow, sinacorWindow);

            }
            catch (Exception e)
            {
                OnWindowManagerError(e);
            }

        }

        #endregion

        #region GetOpenedWindows

        public Dictionary<int, SinacorWindow> GetOpenedWindows()
        {
            return _dctWindowsOpened;
        }

        #endregion

        /// <summary>
        /// Tenta fechar todas as janelas abertas
        /// </summary>
        /// <returns>true se conseguir fechar todas as janelas, false se alguma janela cancelar o fechamento</returns>
        public bool CloseWindows()
        {
            List<SinacorWindow> wc = new List<SinacorWindow>();

            Dictionary<int, SinacorWindow> windows = this.GetOpenedWindows();

            bool closeCanceled = false;
            //varrer as windows e montar uma collection com as windows que não tiverem owner definido
            foreach (KeyValuePair<int, SinacorWindow> keyValuePair in windows)
            {
                SinacorWindow window = keyValuePair.Value;

                if (window != null && window.GetOwner() == null)
                {
                    wc.Add(window);
                }
            }

            //tentar fechar as windows que nao tem "pai"
            foreach (SinacorWindow window in wc)
            {
                if (!CloseWindow(window))
                    closeCanceled = true;
            }

            return !closeCanceled;
        }

        /// <summary>
        /// Fechar todas as filhas de uma window e a ela mesma
        /// </summary>
        /// <param name="window"></param>
        /// <returns></returns>
        private bool CloseWindow(SinacorWindow window)
        {
            bool closeCanceled = false;
            WindowCollection ownedWindows = window.GetOwnedWindows();

            if (ownedWindows == null || ownedWindows.Count == 0)
            {
                //Codigo adicionado para tratar uso indevido do framework por parte dos desenvolvedores
                //tratamento adicionado para o caso onde o desenvolvedor abriu uma window a partir de 
                //outra sem indicar o owner da window sendo aberta.                
                try
                {
                    return window.Close();
                }
                catch
                {
                    return true;
                }
            }
            else
            {
                //varrer as filhas
                foreach (Window w in ownedWindows)
                {
                    //tratamento para o caso de uma SinacorWindow ser pai de uma Window (ex: loading/lock panel)
                    SinacorWindow ownedWindow = w as SinacorWindow;
                    if (ownedWindow != null)
                    {
                        if (!CloseWindow(ownedWindow))
                            closeCanceled = true;
                    }
                }
            }
            if (!closeCanceled)
            {
                //Codigo adicionado para tratar uso indevido do framework por parte dos desenvolvedores
                //tratamento adicionado para o caso onde o desenvolvedor abriu uma window a partir de 
                //outra sem indicar o owner da window sendo aberta.                
                try
                {
                    return window.Close();
                }
                catch
                {
                    return true;
                }
            }
            else
                return false;
        }

        /// <summary>
        /// Força o fechamento de todas as janelas abertas
        /// </summary>
        public void KillWindows()
        {
            Dictionary<int, SinacorWindow> windows = this.GetOpenedWindows();

            foreach (KeyValuePair<int, SinacorWindow> keyValuePair in windows)
            {
                SinacorWindow window = keyValuePair.Value;
                window.Kill();
                //window.Dispose();
            }
        }

        #region IDisposable Members

        public void Dispose()
        {
            if (_dctWindowsOpened != null)
            {
                lock (_dctWindowsOpened)
                {
                    _dctWindowsOpened.Clear();
                }
            }
        }

        #endregion

    }
}


